﻿using System;
using System.Collections.Generic;
using System.Linq;
using QDT.Common.Finance;
using QDT.Core.Assignments;
using QDT.Core.Deals;
using QDT.Core.Projects;
using QDT.P2B.Domain;

namespace QDT.Services.Projects
{
    public sealed class DayRepaymentStrategy : BaseService, IRepayStrategy
    {
        /// <summary>
        /// 构造以天计息，固定结算日回息的还款计划策略类
        /// </summary>
        /// <param name="start">开始日期</param>
        /// <param name="end">结束日期</param>
        /// <param name="settle">结算日</param>
        /// <param name="annualRate">年化率</param>
        /// <param name="money"></param>
        public DayRepaymentStrategy(DateTime start, DateTime end, int settle, double annualRate, decimal money)
        {

            if (start > end) throw new ArgumentException("start 日期不能大于 end日期.");

            if (settle <= 0 || settle > 28) throw new ArgumentException("业务结算日不能是29，30，31号");

            if (money <= 0) throw new ArgumentException("money 必须是大于0的数");

            StartDate = start.Date;
            EndDate = end.Date;
            SettleDay = settle;
            AnnualRate = annualRate;
            Money = money;
        }

        /// <summary>
        /// 还款记录
        /// </summary>
        public IList<CreditRepayment> Records { get; set; }

        /// <summary>
        /// 开始日期
        /// </summary>
        public DateTime StartDate { get; private set; }

        /// <summary>
        /// 结束日期
        /// </summary>
        public DateTime EndDate { get; private set; }

        /// <summary>
        /// 每月的结算日
        /// </summary>
        public int SettleDay { get; private set; }

        /// <summary>
        /// 年化率
        /// </summary>
        public double AnnualRate { get; private set; }

        /// <summary>
        /// 借款金额
        /// </summary>
        public decimal Money { get; private set; }

        /// <summary>
        /// 根据指定天数，计算利息
        /// </summary>
        /// <param name="money">金额</param>
        /// <param name="day">天数</param>
        /// <returns></returns>
        private decimal GetInterest(decimal money, int day)
        {
            return InterestAlgorithm.DailyInterest(day, money, AnnualRate);
        }

        /// <summary>
        /// 根据上期日期，获得下一期的日期
        /// </summary>
        /// <param name="prev">上一期的日期</param>
        /// <returns></returns>
        private DateTime GetNextDate(DateTime prev)
        {

            var d = prev.Day;

            var nextMonth = prev.AddMonths(1);

            var next = d < SettleDay ? new DateTime(prev.Year, prev.Month, SettleDay) : new DateTime(nextMonth.Year, nextMonth.Month, SettleDay);

            if (next > EndDate) return EndDate;

            return next;
        }

        /// <summary>
        /// 根据上期日期，获得下一期到上一期之间的天数
        /// </summary>
        /// <param name="prev"></param>
        /// <returns></returns>
        private int GetNextTime(DateTime prev)
        {

            var next = GetNextDate(prev);

            return Convert.ToInt32(Math.Floor((next - prev).TotalDays));
        }


        #region Implementation of IRepaymentStrategy

        /// <summary>
        /// 获得还款计划表
        /// </summary>
        /// <returns>还款计划表</returns>
        public RepaymentScheme GetRepayTableForProject(IList<DebtorRepayment> repayments)
        {
            var models = new List<RepaymentStage>();

            var cur = StartDate;
            var stage = 0;

            while (true)
            {

                var next = GetNextDate(cur);
                var nextDay = GetNextTime(cur);

                if (next > EndDate) break;

                stage += 1;
                cur = next;

                var model = new RepaymentStage(stage, cur, GetInterest(Money, nextDay), 0, "利息")
                {
                    RepayType = 1
                };

                UpdateStage(repayments, model);

                models.Add(model);

                if (cur == EndDate) break;
            }

            //最后一期为偿还本金期
            var endStage = stage + 1;
            var endModel = new RepaymentStage(endStage, cur, 0, Money, "本金")
            {
                RepayType = 2,
                IsLast = true
            };
            UpdateStage(repayments, endModel);
            models.Add(endModel);

            return new RepaymentScheme(models);
        }

        /// <summary>
        /// 通过债权获得本债权的回款计划
        /// </summary>
        /// <param name="credit">债权，此债权必须是当前项目的债权</param>
        /// <returns></returns>
        public RepaymentScheme GetCreditRepaymenScheme(Credit credit)
        {

            if (credit == null) throw new ArgumentNullException("credit");

            var models = new List<RepaymentStage>();

            var cur = StartDate;
            var stage = 0;

            while (true)
            {

                var next = GetNextDate(cur);
                var nextDay = GetNextTime(cur);

                if (next > EndDate) break;

                stage += 1;
                cur = next;

                var model = new RepaymentStage(stage, cur, GetInterest(credit.Money, nextDay), 0, "利息")
                {
                    RepayType = 1
                };

                UpdateStage(credit, model);

                models.Add(model);

                if (cur == EndDate) break;
            }

            //最后一期为偿还本金期
            var endStage = stage + 1;
            var endModel = new RepaymentStage(endStage, cur, 0, credit.Money, "本金")
            {
                RepayType = 2,
                IsLast = true
            };

            UpdateStage(credit, endModel);
            models.Add(endModel);

            return new RepaymentScheme(models);
        }


        public RepaymentScheme GetAssignmentRepaymenScheme(Assignment assignment)
        {
            if (assignment == null) throw new ArgumentNullException("assignment");
            var models = new List<RepaymentStage>();

            var d = assignment.CreateTime;
            var nextMonth = d.AddMonths(1);

            var cur = d.Day < SettleDay ? new DateTime(d.Year, d.Month, SettleDay) : new DateTime(nextMonth.Year, nextMonth.Month, SettleDay);

            var stage = 0;

            while (true)
            {
                var next = GetNextDate(cur);
                var nextDay = GetNextTime(cur);
                if (next > EndDate) break;

                stage += 1;
                cur = next;

                var model = new RepaymentStage(stage, cur, GetInterest(assignment.Money, nextDay), 0, "利息")
                {
                    RepayType = 1
                };
                models.Add(model);

                if (cur == EndDate) break;
            }

            //最后一期为偿还本金期
            var endStage = stage + 1;
            var endModel = new RepaymentStage(endStage, cur, 0, assignment.Money, "本金")
            {
                RepayType = 2,
                IsLast = true
            };

            models.Add(endModel);

            return new RepaymentScheme(models);

        }

        /// <summary>
        /// 检查此策略下是否可以债权转让
        /// </summary>
        public bool IsAssignment { get { return true; } }

        /// <summary>
        /// 通过债权创建债权转让信息对象
        /// </summary>
        /// <param name="credit"></param>
        /// <param name="feeRate">手续费率</param>
        /// <param name="discountRate">折让率</param>
        /// <returns></returns>
        public Assignment CreateAssignment(Credit credit, double feeRate, double discountRate)
        {
            throw new Exception("Error");
        }

        public Assignment CreateAssignment(Credit credit, decimal money, double feeRate, double discountRate, bool isOffline)
        {

            if (credit.IsRepay || credit.Status == CreditStatus.Completed) throw new Exception("债权已经偿还完，无法转让.");
            if (credit.Money < money) throw new Exception("输入金额大于原债权余额！");
            if (credit.Money <= 0) throw new Exception("持有债权为0，无法转让.");
            var curDate = DateTime.Now;

            if ((curDate.Date - StartDate).TotalDays <= 3 && !isOffline) throw new Exception("购买债权三日内无法债权转让！");

            var assignment = new Assignment
            {
                CreditId = credit.ID,
                UserId = credit.UserID,
                ProjectId = credit.ProjectID,
                FeeRate = feeRate,
                DiscountRate = discountRate,
                Money = money,
                MinLoanMoney = 50
            };

            var project = DbProvider.GetByID<Project>(credit.ProjectID);

            if (project.RepayEndTime < DateTime.Now.Date.AddDays(5)) throw new Exception("返本金前5天不能债权转让！");

            var repays = DbProvider.D<DebtorRepayment>().Where(x => x.ProjectID == project.ID).OrderByDescending(x => x.TrueRepayTime).FirstOrDefault();

            var prevDate = repays == null ? StartDate : repays.RepayTime.Date;
            assignment.CurrentEarning = GetInterest(money, (int)(Math.Floor((curDate.Date - prevDate.Date).TotalDays)));

            assignment.Status = AssignmentStatus.Progress;

            return assignment;
        }

        /// <summary>
        /// 偿还债权
        /// </summary>
        /// <param name="credit">债权</param>
        /// <param name="stage">期次</param>
        public void RepayCredit(Credit credit, int stage)
        {
            throw new NotImplementedException();
        }


        #endregion

        private void UpdateStage(Credit credit, RepaymentStage stage)
        {

            var repayments = credit.Repayments;

            if (stage.Time < credit.CreateTime)
            {
                stage.IsRepay = true;
                stage.ActualTime = stage.Time;
            }

            if (repayments != null)
            {
                var record = repayments.FirstOrDefault(x => x.RepayTime.Date == stage.Time.Date && x.Stage == stage.Stage);

                if (record != null)
                {
                    stage.IsRepay = true;
                    stage.Interest = record.RepayMoney - record.SelfMoney;
                    stage.Principal = record.SelfMoney;
                    stage.ActualTime = record.TrueRepayTime;
                }
            }
        }

        private void UpdateStage(IEnumerable<DebtorRepayment> repayments, RepaymentStage stage)
        {

            if (repayments == null) return;

            var record = repayments.FirstOrDefault(x => x.RepayTime.Date == stage.Time.Date && x.Stage == stage.Stage);

            if (record != null)
            {
                stage.IsRepay = true;
                stage.ActualTime = record.TrueRepayTime;
            }

        }
    }
}
